package com.gwm.compiler;

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import com.google.auto.service.AutoService;
import com.gwm.annotation.layout.IOCWork;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

@AutoService(Processor.class)
public class RoomObservableProcessor extends BaseProcessor{
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Dao.class);
        Set<? extends Element> with = roundEnv.getElementsAnnotatedWith(IOCWork.class);
        TypeSpec.Builder typeSpec = TypeSpec.interfaceBuilder("DBDaoImpl").addModifiers(Modifier.PUBLIC);
        if (elements.size() > 0) {
            for (Element ww : with) {
                IOCWork iocWork = ww.getAnnotation(IOCWork.class);
                AnnotationSpec annotationSpec = AnnotationSpec.builder(com.gwm.annotation.room.Dao.class).addMember("value", "$T.class", ClassName.get("com." + iocWork.value() + ".db", "AppDatabase")).build();
                typeSpec.addAnnotation(annotationSpec);
                for (Element element : elements) {
                    if (element.getKind() == ElementKind.INTERFACE) {
                        execQuery(roundEnv, typeSpec);
                        execInsert(roundEnv, typeSpec);
                        execUpdate(roundEnv, typeSpec);
                        execDel(roundEnv, typeSpec);
                    }
                }
                writeClazz("com." + iocWork.value() + ".roomdatabase", typeSpec.build());
                break;
            }
        }
        return false;
    }

    private void execDel(RoundEnvironment roundEnv, TypeSpec.Builder typeSpec) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Delete.class);
        for (Element element : elements){
            if (element.getKind() == ElementKind.METHOD && element instanceof ExecutableElement){
                String string1 = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString()+"."+element.getEnclosingElement().getSimpleName().toString();
                string1 = string1.replaceAll("\\.","_");
                List<? extends VariableElement> parameters = ((ExecutableElement) element).getParameters();
//                String string = ((ExecutableElement) element).getReturnType().toString();
                MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(string1+"_"+ element.getSimpleName().toString())
                        .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                        .returns(ClassName.get("com.jetpackframework.rxjetpack","Observable"));
                methodSpec.addParameter(ClassName.get("androidx.lifecycle","LifecycleOwner"),"owner");
                if (parameters.size() > 0){
                    for (int i = 0 ; i < parameters.size() ; i++){
                        VariableElement variableElement = parameters.get(i);
                        methodSpec.addParameter(TypeName.get(variableElement.asType()),variableElement.getSimpleName().toString());
                    }
                }
                typeSpec.addMethod(methodSpec.build());
            }
        }
    }

    private void execUpdate(RoundEnvironment roundEnv, TypeSpec.Builder typeSpec) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Update.class);
        for (Element element : elements){
            if (element.getKind() == ElementKind.METHOD && element instanceof ExecutableElement){
                String string1 = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString()+"."+element.getEnclosingElement().getSimpleName().toString();
                string1 = string1.replaceAll("\\.","_");
                List<? extends VariableElement> parameters = ((ExecutableElement) element).getParameters();
//                String string = ((ExecutableElement) element).getReturnType().toString();
                MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(string1+"_"+ element.getSimpleName().toString())
                        .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                        .returns(ClassName.get("com.jetpackframework.rxjetpack","Observable"));
                methodSpec.addParameter(ClassName.get("androidx.lifecycle","LifecycleOwner"),"owner");
                if (parameters.size() > 0){
                    for (int i = 0 ; i < parameters.size() ; i++){
                        VariableElement variableElement = parameters.get(i);
                        methodSpec.addParameter(TypeName.get(variableElement.asType()),variableElement.getSimpleName().toString());
                    }
                }
                typeSpec.addMethod(methodSpec.build());
            }
        }
    }

    private void execInsert(RoundEnvironment roundEnv, TypeSpec.Builder typeSpec) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Insert.class);
        for (Element element : elements){
            if (element.getKind() == ElementKind.METHOD && element instanceof ExecutableElement){
                String string1 = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString()+"."+element.getEnclosingElement().getSimpleName().toString();
                string1 = string1.replaceAll("\\.","_");
                List<? extends VariableElement> parameters = ((ExecutableElement) element).getParameters();
//                String string = ((ExecutableElement) element).getReturnType().toString();
                MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(string1+"_"+ element.getSimpleName().toString())
                        .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                        .returns(ClassName.get("com.jetpackframework.rxjetpack","Observable"));
                methodSpec.addParameter(ClassName.get("androidx.lifecycle","LifecycleOwner"),"owner");
                if (parameters.size() > 0){
                    for (int i = 0 ; i < parameters.size() ; i++){
                        VariableElement variableElement = parameters.get(i);
                        methodSpec.addParameter(TypeName.get(variableElement.asType()),variableElement.getSimpleName().toString());
                    }
                }
                typeSpec.addMethod(methodSpec.build());
            }
        }
    }

    private void execQuery(RoundEnvironment roundEnv, TypeSpec.Builder typeSpec) {
        Set<? extends Element> elements1 = roundEnv.getElementsAnnotatedWith(Query.class);
        for (Element element1 : elements1){
            if (element1.getKind() == ElementKind.METHOD && element1 instanceof ExecutableElement){
                ExecutableElement e = (ExecutableElement) element1;
                String string1 = processingEnv.getElementUtils().getPackageOf(e).getQualifiedName().toString()+"."+element1.getEnclosingElement().getSimpleName().toString();
                string1 = string1.replaceAll("\\.","_");
                List<? extends VariableElement> parameters = e.getParameters();
                String string = e.getReturnType().toString();
                string = string.substring(string.indexOf("<") + 1,string.length() - 1);
                TypeVariableName name = TypeVariableName.get(string);
                ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(ClassName.get("com.jetpackframework.rxjetpack","Observable"),name);
                MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(string1+"_"+e.getSimpleName().toString())
                        .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                        .returns(parameterizedTypeName);
                methodSpec.addParameter(ClassName.get("androidx.lifecycle","LifecycleOwner"),"owner");
                if (parameters.size() > 0){
                    for (int i = 0 ; i < parameters.size() ; i++){
                        VariableElement variableElement = parameters.get(i);
                        methodSpec.addParameter(TypeName.get(variableElement.asType()),variableElement.getSimpleName().toString());
                    }
                }
                typeSpec.addMethod(methodSpec.build());
            }
        }
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> stringSet = new LinkedHashSet<>();
        stringSet.add(Dao.class.getCanonicalName());
        stringSet.add(Insert.class.getCanonicalName());
        stringSet.add(Query.class.getCanonicalName());
        stringSet.add(Update.class.getCanonicalName());
        stringSet.add(Delete.class.getCanonicalName());
        stringSet.add(IOCWork.class.getCanonicalName());
        return stringSet;
    }
}
